package com.franklin.ideaplugin.easytesting.thread.pressuretest;

import com.franklin.ideaplugin.easytesting.entity.PressureTestConfig;
import com.franklin.ideaplugin.easytesting.rpc.entity.MethodInvokeData;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 压力测试任务启动器
 *
 * @author Ye Junhui
 * @since 2023/11/4
 */
@RequiredArgsConstructor
@Getter
public class PressureTestTaskStarter implements Runnable {

    private final PressureTestConfig pressureTestConfig;
    private final String port;
    private final MethodInvokeData methodInvokeData;

    /**
     * 是否正在运行
     */
    private volatile boolean running = true;

    /**
     * 缓存任务线程
     */
    private Map<PressureTestTask,Thread> allThreadMap = Maps.newConcurrentMap();

    @Override
    public void run() {
        //开始时间
        long startTime = System.currentTimeMillis();

        //线程完全启动时间
        long ramUpTotal = pressureTestConfig.getRampUpPeriod() * 1000;

        //总线程数
        Integer threadCount = pressureTestConfig.getThreadCount();

        //持续时间
        Long duration = pressureTestConfig.getDuration();

        //结束时间
        long endTime = -1L;
        if (duration > 0){
            endTime = System.currentTimeMillis() + duration * 1000;
            ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
                    runnable -> {
                        Thread thread = new Thread(runnable, "easy-testing-pressure-test-monitor");
                        thread.setDaemon(true);
                        return thread;
                    }
            );
            long finalEndTime = endTime;
            scheduledExecutorService.scheduleAtFixedRate(
                    () -> {
                        if (System.currentTimeMillis() >= finalEndTime){
                            this.stop();
                        }
                    },
                    0,2,
                    TimeUnit.SECONDS
            );
        }

        //循环创建任务线程并启动
        for (int threadNumber = 0; running && threadNumber < threadCount; threadNumber++) {
            if (threadNumber > 0) {
                //过去时间
                long passTime = System.currentTimeMillis() - startTime;
                //启动延迟时间
                int delayTime = Math.round((ramUpTotal - passTime) / (float) (threadCount - threadNumber));
                //启动器暂停，等待延迟时间结束
                this.pause(Math.max(0, delayTime));
            }
            //是否要终止任务
            if (duration > 0 && System.currentTimeMillis() > endTime){
                break;
            }

            //任务
            PressureTestTask pressureTestTask = new PressureTestTask(
                    port,
                    methodInvokeData,
                    pressureTestConfig.getLoopCount(),
                    new IPressureTestTaskNotifier() {
                        @Override
                        public void notifyFinishOneLoop(int loopIndex) {

                        }

                        @Override
                        public void notifyFinishAll() {

                        }
                    }
            );

            //创建线程
            Thread newThread = new Thread(pressureTestTask, String.valueOf(threadNumber));
            newThread.setDaemon(false);

            //注册
            registerStartedThread(pressureTestTask,newThread);

            //启动
            newThread.start();
        }
    }

    /**
     * 停止任务
     */
    private void stop(){
        this.running = false;
        allThreadMap.forEach((task,thread) -> {
            task.stop();
        });
        allThreadMap.clear();
    }

    /**
     * 暂停线程
     *
     * @param ms
     */
    private void pause(long ms) {
        try {
            TimeUnit.MILLISECONDS.sleep(ms);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 注册已启动的线程
     * @param pressureTestTask
     * @param newThread
     */
    private void registerStartedThread(PressureTestTask pressureTestTask,Thread newThread){
        this.allThreadMap.put(pressureTestTask,newThread);
    }

    /**
     * 监控线程
     */
    private static class MonitorThread implements Runnable{
        @Override
        public void run() {

        }
    }
}
